package com.yuxi.pubInter;



import com.yuxi.common.*;
import com.yuxi.pubInter.aop.SystemLog;
import com.yuxi.pubInter.baseVO.AbstractDataVO;
import com.yuxi.pubInter.baseVO.AbstractPageVO;
import com.yuxi.pubInter.baseVO.DecorateModel;
import com.yuxi.pubInter.baseVO.DecoratePageList;
import com.yuxi.pubInter.exception.SkException;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.web.bind.annotation.*;


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.lang.reflect.ParameterizedType;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @author sunke
 * @Date 2021-01-01 19:35:58
 * @description 接口需要的继承在类
 */
@Api("通用接口")
@RestController
public abstract class RestfulController<
        DTO extends AbstractDTO
        , PageDTO extends AbstractPageDTO
        , T extends BaseIdDoMain<KeyType>
        , TS extends BaseSearch
        , DomainVO extends AbstractDomainVO<KeyType>
        , DetailDomainVO extends DomainVO
        , DetailVO extends AbstractDataVO<DetailDomainVO>
        , ListVO extends AbstractPageVO<DetailDomainVO>, KeyType> extends BaseController {

    public abstract IBaseService<T, TS, KeyType> getBaseService() throws SkException;










    /**
     * 当前模板名称
     *
     * @return
     */
    public String getModelName() {
        return "" ;
    }

    public abstract String getBaseRoute() throws SkException;

    public Class<DTO> getDtoClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[0];
        } catch (Exception e) {

            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[0];
        }
    }

    public Class<PageDTO> getPageDtoClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[1];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[1];
        }
    }

    public Class<T> getDomainClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[2];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[2];
        }
    }

    public Class<TS> getSearchClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[3];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[3];
        }
    }

    public Class<DomainVO> getDomainVOClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[4];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[4];
        }
    }

    public Class<DetailDomainVO> getDomainDetailVOClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[5];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[5];
        }
    }


    public Class<DetailVO> getDetailVOClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[6];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[6];
        }
    }


    public Class<ListVO> getListVOClass() {
        try {
            ParameterizedType type = (ParameterizedType) this.getClass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[7];
        } catch (Exception e) {
            ParameterizedType type = (ParameterizedType) this.getClass().getSuperclass().getGenericSuperclass();
            return (Class) type.getActualTypeArguments()[7];
        }
    }


    /**
     * 列表页面路由
     *
     * @return
     * @throws SkException
     */
    public String getListPageRoute() throws SkException {
        return getBaseRoute() + "list" ;
    }

    public String getAddPageRoute() throws SkException {
        return getBaseRoute() + "add" ;
    }

    public String getPostAddRoute() throws SkException {
        return getBaseRoute() + "postAdd" ;
    }

    public String getModPageRoute() throws SkException {
        return getBaseRoute() + "mod" ;
    }

    public String getPostModRoute() throws SkException {
        return getBaseRoute() + "postMod" ;
    }

    public String getPostDelRoute() throws SkException {
        return getBaseRoute() + "postDelete" ;
    }

    public String getPostDelAllRoute() throws SkException {
        return getBaseRoute() + "postDeleteAll" ;
    }

    public String getDetailPageRoute() throws SkException {
        return getBaseRoute() + "detail" ;
    }


    /**
     * 详情页面
     *
     * @param id
     * @param request
     * @return
     * @throws SkException
     */


    @ApiOperation(value = "详情", notes = "详情", httpMethod = "POST")
    @PostMapping({"detail/{id}"})
    //  @SystemLog(title = "详情页面",businessType=10,method ="getDetail" )
    public SkJsonResult<DetailVO> getDetail(@ApiParam("id") @PathVariable KeyType id
            , HttpServletRequest request, HttpSession session) throws SkException {
        T domain = getBaseService().getDetail(id);


        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();

        jsonResult.setData(map(new DecorateModel(domain), getDetailVOClass()));


        afterDetail(domain,request,session);

        detailExtend(domain, jsonResult.getData(), request, session);



        return jsonResult;

    }

    public void afterDetail(T domain,HttpServletRequest request,HttpSession session) throws SkException{



    }





    @ApiOperation(value = "详情并设置需要填充其它那些数据", notes = "详情并设置需要填充其它那些数据", httpMethod = "POST")
    @PostMapping({"detailAndFullOther/{id}"})

    public SkJsonResult<DetailVO> detailAndFullOther(
            @ApiParam("id") @PathVariable KeyType id
            , @RequestBody @ApiParam("要填充的对象的key") Set<String> fullOtherConfig
            , HttpServletRequest request, HttpSession session) throws SkException {
        T domain = getBaseService().getDetail(id, fullOtherConfig);


        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();

        jsonResult.setData(map(new DecorateModel(domain), getDetailVOClass()));


        detailExtend(domain, jsonResult.getData(), request, session);

        return jsonResult;

    }

    @ApiOperation(value = "详情并设置需要排除填充其它那些数据", notes = "详情并设置需要排除填充其它那些数据", httpMethod = "POST")
    @PostMapping({"detailAndUnFullOther/{id}"})

    public SkJsonResult<DetailVO> detailAndUnFullOther(
            @ApiParam("id") @PathVariable KeyType id
            , @RequestBody @ApiParam("排除要填充的对象的key") Set<String> unFullOtherConfig
            , HttpServletRequest request, HttpSession session) throws SkException {
        T domain = getBaseService().getDetailByUnFullConfigSet(id, unFullOtherConfig);


        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();

        jsonResult.setData(map(new DecorateModel(domain), getDetailVOClass()));


        detailExtend(domain, jsonResult.getData(), request, session);

        return jsonResult;

    }

    /**
     * 详情页面数据扩展
     *
     * @param t
     * @param detailVO
     * @param request
     */

    public void detailExtend(T t, DetailVO detailVO, HttpServletRequest request, HttpSession session) {


    }


    /**
     * 添加数据前检测
     *
     * @param dto
     * @param domain
     * @param session
     * @return
     * @throws SkException
     */
    public boolean beforePostAdd(DTO dto, T domain, HttpServletRequest request, HttpSession session) throws SkException {
        return true;
    }



    @ApiOperation(value = "批量数据添加", notes = "批量数据添加", httpMethod = "POST")
    @PostMapping("postBatchData")
    @SystemLog(title = "批量数据添加", businessType = 1, method = "postBatchData")
    public SkJsonResult<List<DetailVO>> postBatchData(@RequestBody List<DTO> dtoList, HttpServletRequest request, HttpSession session) throws SkException {

        List<DetailVO> voList = SkList.getInstance();
        for (DTO dto : dtoList) {
            SkJsonResult<DetailVO> vo = postAdd(dto, request, session);
            if (StringUtil.isNotObjEmpty(vo.getData())) {
                voList.add(vo.getData());
            }

        }
        return SkJsonResult.ok().setData(voList);

    }




    @ApiOperation(value = "数据添加", notes = "数据添加", httpMethod = "POST")
    @PostMapping("postAdd")
    @SystemLog(title = "数据添加", businessType = 1, method = "postAdd")
    public SkJsonResult<DetailVO> postAdd(@ApiParam(required = true, name = "dto", value
            = "通用基础dto") @Valid @RequestBody DTO dto, HttpServletRequest request, HttpSession session) throws SkException {
        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();
        if (dto == null) {
            return jsonResult;
        }
        T domain = map(dto, getDomainClass());
        if (!beforePostAdd(dto, domain, request, session)) {
            return SkJsonResult.fail("beforePostAdd false;");
        }
        getBaseService().insert(domain);
        jsonResult.setData(map(new DecorateModel<T>(domain), getDetailVOClass()));
        afterPostAdd(dto, domain, jsonResult.getData(), request, session);
        return jsonResult;
    }

    /**
     * 添加插入数据后的操作
     *
     * @param dto
     * @param domain
     * @param detailVO
     * @param request
     * @param session
     * @throws SkException
     */
    public void afterPostAdd(DTO dto, T domain, DetailVO detailVO, HttpServletRequest request, HttpSession session) throws SkException {

    }

    /**
     * 修改提交数据
     *
     * @param id
     * @param dto
     * @param session
     * @return
     * @throws SkException
     */
    @ApiOperation(value = "数据修改", notes = "数据修改", httpMethod = "POST")
    @PostMapping("postMod/{id}")
    @SystemLog(title = "数据修改", businessType = 2, method = "postMod")
    public SkJsonResult<DetailVO> postMod(@ApiParam("id") @PathVariable KeyType id, @Valid @RequestBody DTO dto, HttpServletRequest request, HttpSession session) throws SkException {

        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();
        if (dto == null) {
            return jsonResult;
        }

        T newDomain = map(dto, getDomainClass());
        newDomain.setId(id);
        if (!beforePostMod(dto, newDomain, request, session)) {
            return SkJsonResult.fail("beforePostMod false;");
        }
        T oldDomain = getBaseService().getDetail(id);

        newDomain.setId(id);
        modCompare(dto, newDomain, oldDomain);

        org.springframework.beans.BeanUtils.copyProperties(newDomain, oldDomain, postModNoUpdate(newDomain));
        getBaseService().update(oldDomain);

        oldDomain=getBaseService().getDetail(oldDomain.getId());
        jsonResult.setData(map(new DecorateModel<T>(oldDomain), getDetailVOClass()));
        afterPostMod(dto, oldDomain, jsonResult, request, session);
        return jsonResult;

    }

    /**
     * 比较 新输入数据，和旧数据 ，有时要检测是不是修改的数据是不是已存在
     *
     * @param dto
     * @param newDomain
     * @param oldDomain
     * @throws SkException
     */
    public void modCompare(DTO dto, T newDomain, T oldDomain) throws SkException {

    }


    /**
     * 删除单个
     *
     * @param id
     * @return
     * @throws SkException
     */
    @ApiOperation(value = "数据删除", notes = "数据删除", httpMethod = "POST")
    @PostMapping("postDelete/{id}")
    @SystemLog(title = "数据删除", businessType = 3, method = "postDelete")
    public SkJsonResult<String> postDelete(@ApiParam("id") @PathVariable("id") KeyType id, HttpServletRequest request, HttpSession session) throws SkException {
        if (!beforePostDelete(id, request, session)) {
            return SkJsonResult.fail("beforePostDelete false");
        }



        getBaseService().delete(id);


        afterPostDelete(id, request, session);
        return SkJsonResult.ok();
    }


    @ApiOperation(value = "逻辑数据删除", notes = "逻辑数据删除", httpMethod = "POST")
    @PostMapping("postLogicDelete/{id}")
    @SystemLog(title = "逻辑数据删除", businessType = 3, method = "postLogicDelete")
    public SkJsonResult<String> postLogicDelete(@ApiParam("id") @PathVariable("id") KeyType id, HttpServletRequest request, HttpSession session) throws SkException {
        if (!beforePostLogicDelete(id, request, session)) {
            return SkJsonResult.fail("beforePostDelete false");
        }

        getBaseService().logicDelete(id);

        afterPostLogicDelete(id, request, session);
        return SkJsonResult.ok();
    }



    /**
     * 删除
     *
     * @param dto
     * @return
     * @throws
     */
    @ApiOperation(value = "数据批量删除", notes = "数据批量删除", httpMethod = "POST")
    @PostMapping("postDeleteAll")
    @SystemLog(title = "数据批量删除", businessType = 3, method = "postDeleteAll")
    public SkJsonResult<String> postDeleteAll(@RequestBody IdsDTO dto, HttpServletRequest request, HttpSession session) throws SkException {

        List ids=dto.getIds();
        if (StringUtil.isNullOrEmpty(ids)) {
            return SkJsonResult.fail("删除的条目为空");
        }
        if (!beforePostDeleteAll(ids, request, session)) {
            return SkJsonResult.fail("beforePostDeleteAll false");
        }
        getBaseService().batchDelete(ids);
        afterPostDeleteAll(ids, request, session);
        return SkJsonResult.ok();
    }


    /**
     * 列表页面
     *
     * @param pageDTO
     * @param session
     * @return
     * @throws SkException
     */
    @ApiOperation(value = "数据列表", notes = "数据列表")
    @PostMapping(value = "list")
    //@SystemLog(title = "通用列表接口",businessType=11,method ="list" )
    public SkJsonResult<ListVO> list(@ApiParam("搜索条件") @RequestBody PageDTO pageDTO, HttpServletRequest request, HttpSession session) throws SkException {
        TS search = pageDTOToSearch(pageDTO);
        searchConditionExtend(search, request, session);//附加搜索条件
        listAfter(search,request,session);
        return listSkJsonResult(search, request, session);
    }




    public Set<String> totalMapSet(){
        return null;
    }


    @ApiOperation(value = "统计数据", notes = "统计数据", httpMethod = "POST")
    @PostMapping("totalMap")
    @ResponseBody
    public SkJsonResult<Map<String,Object>> totalMap(@ApiParam("搜索条件") @RequestBody PageDTO pageDTO
            ,  HttpServletRequest request,HttpSession session) throws SkException {

        SkJsonResult<Map<String,Object>> jsonResult = SkJsonResult.ok();

        TS search=map(pageDTO,getSearchClass());
        searchConditionExtend(search,request,session);
        Map<String, Object> totalMap=null;
        if(StringUtil.isNotObjEmpty(totalMapSet())) {
            totalMap = getBaseService().totalMap(totalMapSet(), search);
        }
        afterTotalMap(totalMap,search);
        jsonResult.setData(totalMap);

        return jsonResult;

    }


    public void afterTotalMap(Map<String,Object> totalMap ,TS search) throws SkException{

    }




    /**
     * 列表页面
     *
     * @param pageDTO
     * @param session
     * @return
     * @throws SkException
     */
    @ApiOperation(value = "列表数量", notes = "列表数量")
    @PostMapping(value = "listCount")
    public SkJsonResult<Integer> listCount(@ApiParam("搜索条件") @RequestBody PageDTO pageDTO, HttpServletRequest request, HttpSession session) throws SkException {
        TS search = pageDTOToSearch(pageDTO);
        searchConditionExtend(search, request, session);//附加搜索条件

        return SkJsonResult.ok().setData(getBaseService().getCountBySearch(search));
    }



    /**
     * 搜索到到某一个详情
     *
     * @param pageDTO
     * @param session
     * @return
     * @throws SkException
     */
    @ApiOperation(value = "搜索到到某一个详情", notes = "搜索到到某一个详情")
    @PostMapping(value = "detailBySearch")
    public SkJsonResult<DetailVO> detailBySearch(@ApiParam("搜索条件") @RequestBody PageDTO pageDTO, HttpServletRequest request, HttpSession session) throws SkException {
        TS search = pageDTOToSearch(pageDTO);
        searchConditionExtend(search, request, session);//附加搜索条件

        SkJsonResult<DetailVO> jsonResult = SkJsonResult.ok();

        T domain=getBaseService().getDetailBySearch(search);

        jsonResult.setData(map(new DecorateModel(domain), getDetailVOClass()));


        detailExtend(domain, jsonResult.getData(), request, session);



        return jsonResult;
    }



    public void listAfter(TS search, HttpServletRequest request, HttpSession session) throws SkException{



    }


    public SkJsonResult<ListVO> listSkJsonResult(TS search, HttpServletRequest request, HttpSession session) throws SkException {
        SkJsonResult<ListVO> jsonResult = SkJsonResult.ok();
        fillListData(search, jsonResult, request, session);
        listPageExtend(search, jsonResult.getData(), request, session);
        return jsonResult;
    }


    public TS pageDTOToSearch(PageDTO pageDTO) {
        return map(pageDTO, getSearchClass());
    }


    /**
     * 排除不要修改的字段
     *
     * @param domain
     * @return
     */
    protected String[] postModNoUpdate(T domain) {
        String[] excludeFiledArr = BeanUtils.getNullPropertyNames(domain);
        if (excludeFiledArr == null) {
            excludeFiledArr = new String[0];
        }
        String excludeFiledStr = addExcludeUpdateField(domain);
        if (excludeFiledStr == null || "".equals(excludeFiledStr)) {
            return excludeFiledArr;
        }
        String[] excludeFiled = excludeFiledStr.split(",");
        for (int i = 0; i < excludeFiled.length; i++) {
            excludeFiledArr = ArrayUtils.add(excludeFiledArr, excludeFiled[i]);
        }
        return excludeFiledArr;
    }

    /**
     * 添加额外不要修改的字段
     *
     * @param t
     * @return
     */
    public String addExcludeUpdateField(T t) {
        return "" ;
    }

    /**
     * 修改数据前
     *
     * @param dto
     * @param domain
     * @param session
     * @return
     * @throws SkException
     */
    public boolean beforePostMod(DTO dto, T domain, HttpServletRequest request, HttpSession session) throws SkException {
        return true;
    }

    /**
     * 修改插入数据后的操作
     *
     * @param dto
     * @param domain
     * @param jsonResult
     * @param session
     * @throws SkException
     */
    public void afterPostMod(DTO dto, T domain, SkJsonResult<DetailVO> jsonResult, HttpServletRequest request, HttpSession session) throws SkException {

    }


    /**
     * 删除数据前
     *
     * @param id
     * @param session
     * @return
     * @throws SkException
     */
    public boolean beforePostDelete(KeyType id, HttpServletRequest request, HttpSession session) throws SkException {
        return true;
    }

    public boolean beforePostLogicDelete(KeyType id, HttpServletRequest request, HttpSession session) throws SkException {
        return true;
    }

    /**
     * 删除数据后的操作
     *
     * @param id
     * @param session
     * @throws SkException
     */
    public void afterPostDelete(KeyType id, HttpServletRequest request, HttpSession session) throws SkException {

    }


    public void afterPostLogicDelete(KeyType id, HttpServletRequest request, HttpSession session) throws SkException {

    }


    /**
     * 删除数据前
     *
     * @param ids
     * @param session
     * @return
     * @throws SkException
     */
    public boolean beforePostDeleteAll(List<KeyType> ids, HttpServletRequest request, HttpSession session) throws SkException {
        return true;
    }

    /**
     * 删除数据后的操作
     *
     * @param ids
     * @param session
     * @throws SkException
     */
    public void afterPostDeleteAll(List<KeyType> ids, HttpServletRequest request, HttpSession session) throws SkException {

    }


    /**
     * 列表页面增加
     *
     * @param search
     * @param listVO
     * @param session
     * @throws SkException
     */
    public void listPageExtend(TS search, ListVO listVO, HttpServletRequest request, HttpSession session) throws SkException {


    }

    /**
     * 列表数据
     *
     * @param search
     * @param jsonResult
     * @param request
     * @param session
     * @throws SkException
     */
    public void fillListData(TS search, SkJsonResult<ListVO> jsonResult, HttpServletRequest request, HttpSession session) throws SkException {
        Map<String, Object> skMap = getBaseService().paginate(search);
        DecoratePageList pageList = new DecoratePageList(decorateList((List<T>) skMap.get(PubConst.PAGINATE_DATA_KEY)), (PageBean) skMap.get(PubConst.PAGINATE_PAGE_KEY));
        jsonResult.setData(map(pageList, getListVOClass()));
    }



    /**
     * 装饰list数据
     *
     * @param dataList
     * @return
     */
    public List<T> decorateList(List<T> dataList) {
        return dataList;
    }

    /**
     * 搜索条件附加
     *
     * @param search
     * @throws SkException
     */
    public void searchConditionExtend(TS search, HttpServletRequest request, HttpSession session) throws SkException {

    }

/*

    @ApiOperation(value = "导出数据", notes = "导出数据", httpMethod = "POST")
    @PostMapping("exportData")
    @SystemLog(title = "导出数据", method = "exportData", businessType = 5)
    public void exportContract(@RequestBody PageDTO pageDTO, HttpServletRequest request, HttpServletResponse response) throws Exception {


        EasyExcelUtil.excelResponseData(StringUtil.isNullOrEmpty(getModelName()) ? "导出数据" : getModelName(), response, mapList(getBaseService().getAllList(map(pageDTO, getSearchClass())),
                ContractBackMoneyExport.class), ContractBackMoneyExport.class);

    }
*/







}